﻿#include <CXXLTHREAD.HPP>
#include <Sys\RenderUnit\SysRenderDatagram\CUBEMAPTEXTURERD.HPP>

using namespace CxxlMan;
using namespace Cxxl3D;

// 以下兩個函數定義在 MipmapCreate.cpp

bool MipmapCreate(unsigned char ***Mipmap, unsigned int **WidthHeight, unsigned int *LevelNum,
                  const unsigned char *SrcMap, const unsigned int SrcWidth, 
                  const unsigned int SrcHeight, const unsigned int bpp);

void MipmapDelete(unsigned char **Mipmap, unsigned int *WidthHeight, unsigned int LevelNum);


#define CCubemapTextureRD_ClassID L"5B37FBB828C947e4A95E25F5B84FD121"

cxxlObject *cxxlFASTCALL New_CCubemapTextureRD();
cxxlPreserve2 *cxxlFASTCALL Regenera_CCubemapTextureRD(const wchar_t *ClassID);

class CCubemapTextureRD:public ICubemapTextureRD
{

  struct tagAttr 
  {    
    unsigned char m_BlendMode;

    unsigned char m_bMipmapMode:2;   // 貼圖的 Mipmap 模式  0:None(內定) 1:Bilinear 2:Trilinear 3:Anisotropic filter

    // 自動產生貼圖座標
    // 參數以 D3D9 的 D3DTSS_TCI 為功能基準
    // 0:以頂點在 View 座標系中的 x,y 值作為貼圖的 u,v，頂點的 z 值不使用，內定值(CAMERASPACEPOSITION)
    // 1:以頂點在 View 座標系中的 法向量值 作為貼圖的座標值(CAMERASPACENORMAL)
    // 2:以 反射向量值 作為貼圖的座標值，其值在 View 座標系中的頂點位置和法向量來計算(CAMERASPACEREFLECTIONVECTOR)
    // 3:以頂點在 View 座標系中 法向量的 x,y 值轉換到 [0,1] 區間，作為貼圖的座標值(SPHEREMAP)
    // -1:不使用自動產生貼圖座標
    char m_bAutoTexCoordIndexMode:2; 

    unsigned char m_bTextureAddrMode:2; // 設定貼圖座標模式  0:Repeat(內定) 1:Mirror 2:ClampEdge 3:ClampBorder

    union
    {
      struct 
      {
        char R,G,B,A;
      };
      unsigned int Color;
    }m_TextureAddrModeColor; // m_bTextureAddrMode 指定為 ClampBorder 時，所用的顏色，內定為 0,0,0,0

    tagAttr()
    {
      m_BlendMode = 0;
      m_bMipmapMode = 0;
      m_bAutoTexCoordIndexMode = 0;
      m_bTextureAddrMode = 0;
      m_TextureAddrModeColor.Color = 0;
    }
  }Attr;

  bool m_bTextureMatrixIsNULL; // 用於永續儲存檢查 m_TextureMatrix_cPtr 是否為 NULL

  unsigned char ***Mipmap;
  unsigned int **WidthHeight;
  unsigned int LevelNum;

  bool m_isCrushable;
  mutable bool m_isCallRetrieve;
  wchar_t *m_CubemapTextureID;
  P2Smart_Ptr<IRetrieveCubemapTexture> m_IRetrieveCubemapTexture_Ptr;
  CSmart_Ptr<const C3DMatrix4x4> m_TextureMatrix_cPtr;

  void cxxlFASTCALL MipmapClear()
  {
    if(Mipmap != NULL)
    {
      for(int i = 0; i < 6; ++i)
        MipmapDelete(Mipmap[i], WidthHeight[i], LevelNum);
    }

    delete [] Mipmap;
    delete [] WidthHeight;

    Mipmap = NULL;
    WidthHeight = NULL;
    LevelNum = 0;
  }

  // 當 Deposit 被清除呼叫之後，會被叫用
  void cxxlFASTCALL haveClearDepositEvent()
  {
  }

  // 當 SetDeposit() 呼叫之後，會被叫用
  void cxxlFASTCALL haveSetDepositEvent()
  {
    this->csPreserve.Enter();

    if(m_isCrushable) 
      MipmapClear();

    this->csPreserve.Leave();
  }

  virtual void cxxlFASTCALL SysInit(const UNICODE_String &CubemapTextureID)
  {
    m_CubemapTextureID = new wchar_t[CubemapTextureID.StrLength()+1];
    wcscpy(m_CubemapTextureID, (const wchar_t*)CubemapTextureID);
  }

  // 取得天空盒貼圖資料識別，IRetrieveTexture 的實作須有能力藉此將正確的頂點資料由 MipmapMake() 置入
  virtual CxxlMan::UNICODE_String cxxlFASTCALL GetCubemapTextureID()
  {
    CriticalSecHandle AutoCSH(csPreserve);
    return m_CubemapTextureID;
  }

  void cxxlFASTCALL SetBlendMode(const unsigned char BlendMode)
  {
    csPreserve.Enter();
    Attr.m_BlendMode = BlendMode;
    csPreserve.Leave();    
  }

  void cxxlFASTCALL SetTextureMatrix(const CxxlMan::CSmart_Ptr<const C3DMatrix4x4> &TextureMatrix_Arg)
  {
    csPreserve.Enter();
    m_TextureMatrix_cPtr = TextureMatrix_Arg;
    csPreserve.Leave();    
  }


  // 設定貼圖座標模式 Mode = 0:Repeat(內定) 1:Mirror 2:ClampEdge 3:ClampBorder
  virtual void cxxlFASTCALL TextureAddrMode(unsigned char Mode)
  {
    csPreserve.Enter();
    Attr.m_bTextureAddrMode = Mode;
    csPreserve.Leave();    
  }

  // TextureAddrMode() 指定為 ClampBorder 時，所用的顏色，內定為 0,0,0,0
  virtual void cxxlFASTCALL TextureClampBorderColor(char R, char G, char B, char A)
  {
    csPreserve.Enter();
    Attr.m_TextureAddrModeColor.R = R;
    Attr.m_TextureAddrModeColor.G = G;
    Attr.m_TextureAddrModeColor.B = B;
    Attr.m_TextureAddrModeColor.A = A;
    csPreserve.Leave();    
  }

  // 使用 dataCubemapTexture_2D 建立 6 面的 Mipmap
  virtual void cxxlFASTCALL MipmapMake(const CxxlMan::CSmart_Ptr<const dataCubemapTexture_2D> &CubemapTexture_Arg)
  {
    C3D_UINT Width, Height;
    CSmart_Array<const tagPIXEL> Front(NULL);
    CSmart_Array<const tagPIXEL> Back(NULL);
    CSmart_Array<const tagPIXEL> Left(NULL);
    CSmart_Array<const tagPIXEL> Right(NULL);
    CSmart_Array<const tagPIXEL> Up(NULL);
    CSmart_Array<const tagPIXEL> Down(NULL);
   
    CubemapTexture_Arg->GetData(Width, Height, Front, Back, Left, Right, Up, Down);
    if(Width != 0 && Height != 0 && 
      Front.isNULL() == false &&
      Back.isNULL() == false &&
      Left.isNULL() == false &&
      Right.isNULL() == false &&
      Up.isNULL() == false &&
      Down.isNULL() == false)
    {
      unsigned char **vMipmap;
      unsigned int *vWidthHeight;
      unsigned int vLevelNum;

      MipmapClear();
      CSmart_Array<const tagPIXEL> *Texture_Array[6] = {&Front, &Back, &Left, &Right, &Up, &Down};
      this->csPreserve.Enter();
      Mipmap = new unsigned char **[6];
      WidthHeight = new unsigned int *[6];
      for(int i = 0; i < 6; ++i)
      {
        MipmapCreate(&vMipmap, &vWidthHeight, &vLevelNum,(const unsigned char *)(const tagPIXEL *)*Texture_Array[i], Width, Height, 4);

        Mipmap[i] = vMipmap;
        WidthHeight[i] = vWidthHeight;
        LevelNum = vLevelNum;
      }
      Modified();
      this->csPreserve.Leave();
    }
    m_isCallRetrieve = false;   
  }

  // 供 Render 單元取得屬性資料 
  virtual void cxxlFASTCALL GetAttr(unsigned char &TextureAddrMode, unsigned int &TextureClampBorderRGBA, unsigned char &MipmapMode, char &AutoTexCoordIndexMode, unsigned char &BlendMode, CxxlMan::CSmart_Ptr<const C3DMatrix4x4> &TextureMatrix_Arg) const
  {
    csPreserve.Enter();
    TextureAddrMode = Attr.m_bTextureAddrMode;
    TextureClampBorderRGBA = Attr.m_TextureAddrModeColor.Color;
    MipmapMode = Attr.m_bMipmapMode;
    AutoTexCoordIndexMode = Attr.m_bAutoTexCoordIndexMode;
    BlendMode = Attr.m_BlendMode;
    TextureMatrix_Arg = m_TextureMatrix_cPtr;
    csPreserve.Leave();
  }

  // 供 Render 單元取得 6 面的 Mipmap 資料
  virtual bool cxxlFASTCALL GetMipmap(const unsigned char ****Mipmaps, const unsigned int ***WidthHeights, unsigned int *LevelNumber) const
  {

    if(*Mipmaps == NULL)
    {
      if(m_isCallRetrieve == false)
      {
        m_isCallRetrieve = true;

        csPreserve.Enter();
        Smart_Ptr<IRetrieveCubemapTexture> IRetrieveCubemapTexture_Ptr = m_IRetrieveCubemapTexture_Ptr;
        csPreserve.Leave();

        if(IRetrieveCubemapTexture_Ptr.isNULL() == false)
          IRetrieveCubemapTexture_Ptr->GetTexture(const_cast<CCubemapTextureRD*>(this));
        else
          m_isCallRetrieve = false;
      }
      return false;
    }

    csPreserve.Enter();
    *Mipmaps = (const unsigned char ***)Mipmap;
    *WidthHeights = (const unsigned int **)WidthHeight;
    *LevelNumber = LevelNum;
    csPreserve.Leave();

    return true;
  }


  // 通知其儲存的 Mipmap 所依據的 dataCubemapTexture_2D 資料已被修改，只有 Crushable() 設定過才能再去取得，否則仍使用儲存的 Mipmap。
  // 若未使用 Crushable()，不如再用 MipmapMake() 更新資料
  virtual void cxxlFASTCALL Modified()
  {
    this->ClearDeposit();
  }

  // 指示當 Render 單元取出 Mipmap 資料後，可以將之清除，內定是不清除的。
  // 須提供一個 IRetrieveCubemapTexture，作為須取出 Mipmap 資料時的處理方法
  // IRetrieveCubemapTexture_Arg 不可為 NULL，否則 Crushable() 呼叫無效
  virtual void cxxlFASTCALL Crushable(const CxxlMan::Smart_Ptr<IRetrieveCubemapTexture> &IRetrieveCubemapTexture_Arg)
  {
    if(IRetrieveCubemapTexture_Arg.isNULL())
      return;

    this->csPreserve.Enter();
    m_isCrushable = true;
    m_IRetrieveCubemapTexture_Ptr = IRetrieveCubemapTexture_Arg;
    this->csPreserve.Leave();
  }

  // 貼圖的 Mipmap 模式 Mode = 0:None(內定) 1:Bilinear 2:Trilinear 3:Anisotropic filter
  virtual void cxxlFASTCALL TextureMipmapMode(unsigned char Mode)
  {
    this->csPreserve.Enter();
    Attr.m_bMipmapMode = Mode;
    this->csPreserve.Leave();    
  }

  // 自動產生貼圖座標
  // Mode 參數以 D3D9 的 D3DTSS_TCI 為功能基準
  // Mode = 0:以頂點在 View 座標系中的 x,y 值作為貼圖的 u,v，頂點的 z 值不使用，內定值(CAMERASPACEPOSITION)
  // Mode = 1:以頂點在 View 座標系中的 法向量值 作為貼圖的座標值(CAMERASPACENORMAL)
  // Mode = 2:以 反射向量值 作為貼圖的座標值，其值在 View 座標系中的頂點位置和法向量來計算(CAMERASPACEREFLECTIONVECTOR)
  // Mode = 3:以頂點在 View 座標系中 法向量的 x,y 值轉換到 [0,1] 區間，作為貼圖的座標值(SPHEREMAP)
  // -1:不使用自動產生貼圖座標
  virtual void AutoTexCoordIndex(char Mode)
  {
    this->csPreserve.Enter();
    Attr.m_bAutoTexCoordIndexMode = Mode;
    this->csPreserve.Leave();    
  }


protected:
  // 儲存容器藉此取得要永續存取的屬性資料
  // 延伸類別須以 virtual 的形式覆寫
  // 並且須呼叫上層類別的此函數
  virtual bool cxxlFASTCALL Ref(Filter *F)
  {
    if(F != NULL && ICubemapTextureRD::Ref(F))
      return true;
    else
      return false;


    if(F != NULL && ICubemapTextureRD::Ref(F))
    {
      if(F->Ref(&m_isCrushable, 1, L"isCrushable") == false)
        return false;

      if(F->Ref((char *)&Attr,sizeof(Attr),L"CubemapTextureATTR") == false) 
        return false;

      LoadFilter *pLoadFilter = dynamic_cast<LoadFilter*>(F);
      if(pLoadFilter == NULL) // 表示 F 是 SaveFilter
      {        
        F->Ref(m_CubemapTextureID, wcslen(m_CubemapTextureID) + 1, L"CubemapTextureID");

        if(m_TextureMatrix_cPtr.isNULL())
          F->Ref((C3D_FLOAT*)(const C3DMatrix4x4*)m_TextureMatrix_cPtr,0,L"TextureMatrix");
        else
          F->Ref((C3D_FLOAT*)(const C3DMatrix4x4*)m_TextureMatrix_cPtr,9,L"TextureMatrix");
      }
      else // 表示 F 是 LoadFilter
      {
        if( !pLoadFilter->LoadRef( (const wchar_t**)&m_CubemapTextureID, L"CubemapTextureID") )
          return false;

        if(pLoadFilter->isChk()) // 檢查階段
        {
          const C3D_FLOAT *data;
          size_t Count;
          if(pLoadFilter->LoadChk(&data,&Count,L"TextureMatrix") == false)
            return false;
          if(Count == 0)
            m_bTextureMatrixIsNULL = true;
          else
            m_bTextureMatrixIsNULL = false;
        }
        else // 實際讀取階段
        {
          if(m_bTextureMatrixIsNULL)
            m_TextureMatrix_cPtr.Destroy();
          else
          {
            m_TextureMatrix_cPtr = new C3DMatrix4x4;
            F->Ref((C3D_FLOAT*)(const C3DMatrix4x4*)m_TextureMatrix_cPtr,9,L"TextureMatrix");
          }
        }

      }

      if(pLoadFilter->isChk() == false) // 能執行這，表示已正確 Load 了
        this->Modified(); // 對於 m_isCrushable 為 false，load 別的 m_CubemapTextureID 的永續資料，也沒法變動已呈現的 3D 樣貌

      return true;
    }
    else
      return false;

  }
public:
  // Constructor
  CCubemapTextureRD()
    :cxxlObject(Spirit_Easy),
    cxxlPreserve2(CCubemapTextureRD_ClassID), // 在建構時指定 ClassID
    m_IRetrieveCubemapTexture_Ptr(NULL,this,L"RetrieveCubemapTexture"),
    m_TextureMatrix_cPtr(NULL)
  {
    Mipmap = NULL;
    WidthHeight = NULL;
    LevelNum = 0;
    m_isCallRetrieve = false;
    m_isCrushable = false;
  }

  // Destructor
  ~CCubemapTextureRD()
  {
    delete [] m_CubemapTextureID;
  }

  friend cxxlObject *cxxlFASTCALL New_CCubemapTextureRD();
  friend cxxlPreserve2 *cxxlFASTCALL Regenera_CCubemapTextureRD(const wchar_t *ClassID);
};

cxxlObject *cxxlFASTCALL New_CCubemapTextureRD()
{
  return new CCubemapTextureRD();
}

cxxlPreserve2 *cxxlFASTCALL Regenera_CCubemapTextureRD(const wchar_t *ClassID)
{
  if(wcscmp(CCubemapTextureRD_ClassID, ClassID) == 0)
    return new CCubemapTextureRD();
  else
    return NULL;
}
